layui.define(['jquery', 'element', 'layer'], function(exports){
  const $ = layui.jquery,
    element = layui.element,
    layer = layui.layer;
  
  function Menu({ selector, menuTree, clickClose }) {
    this.clickClose = clickClose ? clickClose : false;
    if (!selector) return layer.alert('请给一个选择器', { title: '错误', icon: 2 });
    this.selector = selector;
    if (!menuTree) return layer.alert('请给一个json的菜单树', { title: '错误', icon: 2 });
    this.render(menuTree).listen();
  };
  Menu.prototype = {
    constructor: Menu,
    listen: function() {
      if (this.initialized !== true) {
        return false;
      }
      /** 
       * 1.鼠标悬浮导航和点击导航时基本样式改变 
       * 设计逻辑：1.子菜单采用延迟显示和延迟隐藏，同一子菜单的显示和隐藏任务同一时间最多一个存在。
       * 					2.鼠标悬浮头部菜单有鼠标悬浮效果，对应的子菜单显示任务启动；
       * 						鼠标移出头部菜单移除悬浮效果，隐藏对应子菜单任务启动；
       *						鼠标悬浮子菜单对应父菜单有悬浮效果，移除隐藏子菜单任务；
       * 						鼠标移出子菜单移除悬浮效果，隐藏子菜单任务启动；
       * */
      let timers = {}, clickClose = false, root = this.selector;
      $(root).on('mouseover', '[nav-id]', function(e) {
        if (!clickClose) {
          const id = $(this).attr('nav-id');
          clearTimeout(timers[id]);
          $(root + ' .nav-header [nav-id]').each(function() {
            $(this)[$(this).attr('nav-id') === id ? 'addClass' : 'removeClass']('hover');
          });
          if (!$(this).hasClass('nav-sub-menu')) {
            const hasC = $(this).attr('has-children');
            if (hasC !== undefined) {
              clearTimeout(timers[id]);
              timers[id] = setTimeout(() => {		
                let contentDom = $(`${root} .nav-content [nav-id="${id}"]`);
                contentDom.stop(true, true).animate({
                  bottom: `-${contentDom.outerHeight() - 1}px`
                }, 200);
              }, 150);
            }
          }
        }
      }).on('mouseout', '[nav-id]', function() {
        if (!clickClose) {
          const id = $(this).attr('nav-id');
          clearTimeout(timers[id]);
          $(root + ' .nav-header [nav-id]').removeClass('hover');
          timers[id] = setTimeout(function() {
            $(`${root} .nav-content [nav-id="${id}"]`).stop(true, true).animate({
              bottom: 0
            }, 200);
          }, 150);
        }	
      });
      /* 是否开启点击关闭富菜单 */
      if (this.clickClose) {
        $(root).on('click', '.nav-content dd a', function() { // 鼠标点击子菜单立马关闭
          clickClose = true;
          const id = $(root + ' .nav-content [nav-id]').has(this).attr('nav-id');
          $(root + ' .nav-header [nav-id]').removeClass('hover');
          $(`${root} .nav-content [nav-id="${id}"]`).stop(true, true).animate({
            bottom: 0
          }, 200, function() { clickClose = false; });
        });
      }
      /* 2.菜单点击选中效果添加 */
      $(root).on('click', '.nav-header li:not([has-children])', function() {
        const self = this;
        $(root + ' .nav-header [nav-id]').each(function() {
          $(this)[self === this ? 'addClass' : 'removeClass']('selected');
        });
      }).on('click', '.nav-content dd a', function() {
        let _p = $(root + ' .nav-content [nav-id]').has(this);
        let id = _p.attr('nav-id');
        $(root + ' .nav-header [nav-id]').each(function() {
          $(this)[id == $(this).attr('nav-id') ? 'addClass' : 'removeClass']('selected');
        });
        $(root + ' .nav-content .selected').removeClass('selected');
        $(this).addClass('selected');
      });
      /* 3.矫正子菜单位置,没次窗体大小改变矫正一次 */
      function resizeSubNav() {
        // 重置子菜单纵向
        let offset = $(root).outerHeight() + $(root).offset().top;
        $(`${ root } .nav-content`).css('top', offset + 'px');
        // 重置子菜单横向
        const vw = $(window).width();
        $(root + ' .nav-header [nav-id]').each(function() {
          const id = $(this).attr('nav-id');
          let contentDom = $(`${root} .nav-content [nav-id="${id}"]`);
          if (contentDom.length > 0) {
            const itemW = $(this).outerWidth();
            const left = $(this).offset().left;
            const innerDom = contentDom.children('.inner');
            const smw = innerDom.outerWidth();
            if (smw < vw) {
              if (smw < left + itemW) {
                innerDom.css('margin', `0 0 0 ${ left + itemW - smw }px`);
              } else {
                innerDom.css('margin', '0 0 0 25px');
              }
            } else {
              innerDom.css('margin', '0 15px');
            }
          }
        });
      }
      $(window).resize((function() {
        let timer = setTimeout(resizeSubNav, 200);
        return function() {
          clearTimeout(timer);
          timer = setTimeout(resizeSubNav, 200);
        }
      })());
      /* 监听菜单点击，改变地址hash */
			$(root).on('click', 'a[route]', function() {
        const toHash = $(this).attr('route');
        if (toHash === decodeURIComponent(location.hash).substr(1)) {
          location.hash = '';
        }
				location.hash = encodeURIComponent(toHash);
			});
    },
    order: function(arr, key) {
      if (arr.length > 0 && arr[0][key]) {
        arr.sort((a, b) => a[key] > b[key] ? 1 : -1);
      }
    },
    createHtmlStr: function(menuTree) {
      let subMenu = [];
      // 一级菜单排序
      this.order(menuTree, 'order');
			// 一级菜单生成
			// nav-id使一级菜单和富菜单关联的标识，
			// has-children标识一级菜单是否有富菜单
			// a标签route属性标识该菜单是叶子菜单，有跳转行为
			let headerStr = menuTree.reduce(function(header, item, index) {
				const { title , hash , children } = item;
				const hasChildren = (children && children.length > 0) ? true : false;
				if (hasChildren) {
					subMenu.push({ pid: hash, children, pindex: index });
				}
				return `${ header }
                <li class="${ index===0 ? 'selected' : '' }" nav-id="${ hash }" ${ hasChildren ? 'has-children' : ''}>
                  <a href="javascript:;" ${ hasChildren ? '' : 'route="' + hash + '"' }>${ title }</a>
                </li>`;
			}, '<ul>') + '</ul>';
			// 子菜单生成
			let subStr = subMenu.reduce((sub, item, index) => {
				const { pid , pindex , children: subArr } = item;
        // 二级菜单排序
        this.order(subArr, 'order');

				const subStr = subArr.reduce((htmlStr, second, i) => {
					let { title: label, hash } = second;
					if (second.children && second.children.length > 0) {
            // 三级菜单排序
            this.order(second.children, 'order');
            
						htmlStr += second.children.reduce(function(s, third, _i) {
							let { title: label, hash } = third;
							let f = (pindex === 0) && (i === 0) && (_i === 0);
							return `${ s }<dd><a route="${ hash }" class="${ f ? 'selected' : '' }" href="javascript:;">${ label }</a></dd>`;
						}, `<dl><dt><a href="javascript:;">${ label }</a></dt>`) + '</dl>';
					} else {
						let f = (pindex === 0) && (i === 0);
						htmlStr += `<dl><dd><a route="${ hash }" class="${ f ? 'selected' : '' }" href="javascript:;">${ label }</a></dd></dl>`;
					}
					return htmlStr;
				}, '');
				return `${ sub }
                <div class="nav-sub-menu" nav-id="${ pid }">
                  <div class="inner">
                    ${ subStr }
                  </div>
                </div>`;
			}, '');
      // 包裹菜单内容
      headerStr = `<div class="nav-header">${headerStr}</div>`;
      subStr = `<div class="nav-content">${subStr}</div>`;
      return { headerStr, subStr }
    },
    render: function(menuTree) {
      const { headerStr, subStr } = this.createHtmlStr(menuTree);
      $(this.selector).append(headerStr).append(subStr);
      this.initialized = true;
      return this;
    }
  };

  // 将树状菜单扁平化，转换成一维hash和href关系
  function flattenMenu(menu, obj) {
    return menu.reduce(function(map, elem) {
      if (elem.children && elem.children.length > 0) {
        flattenMenu(elem.children, map);
      } else {
        elem.label = elem.title;
        map[elem.hash] = elem;
      }
      return map;
    }, obj ? obj : {});
  }

  function Tab({ layFilter, maxOpens, menuTree }) {
    this.layFilter = layFilter;
    this.maxOpens = maxOpens ? maxOpens : 5;
    this.openTab = [];
    this.showHash = '';
    this.hashMap = flattenMenu(menuTree);
    this.listen();
  }
  Tab.prototype = {
    constructor: Tab,
    listen: function() {
      let self = this;
      /* tab切换事件监听 */
      element.on(`tab(${this.layFilter})`, function(data){
				self.change($(this).attr('lay-id'));
			});
      /* tab删除事件监听 */
			element.on(`tabDelete(${this.layFilter})`, function(data){
				self.del($(this).parent().attr('lay-id'));
			});
      /* 页面加载时hash定位 */
			$(document).ready(function() {
        // 默认打开的先打开
        const openList = Object.values(self.hashMap).filter(elem => new Number(elem.open).valueOf() === 1);
        const size = Math.min(self.maxOpens, openList.length);
        if (size > 0) {
          for (let i = 0; i < size; i++) {
            self.create(openList[i]);
          }
          self.change(openList[0].hash);
        }
        
				let hash = decodeURIComponent(location.hash);
				if (hash) {
          hash = hash.substr(1);
					const t = self.hashMap[hash];
					if (t) {
            if (self.openTab.indexOf(hash) > -1) {
              self.change(hash);
            } else {
              self.create({hash, label: t.label, href: t.href});
            }
					}
				}
			});
      /* hash变化监听 */
			$(window).on('hashchange', function(e) {
				let hash = decodeURIComponent(location.hash);
				if (hash === '') {
					return false;
				} else {
          hash = hash.substr(1);
          if (hash === self.showHash) {
            return false;
          } else {
            let t = self.hashMap[hash];
            if (t) {
              if (self.openTab.indexOf(hash) > -1) {
                self.change(hash);
              } else {
                self.create({hash, label: t.label, href: t.href});
              }
            }
          }
        }
			});
    },
    create: function({ hash, label, href }) {
      if (this.openTab.length >= this.maxOpens) {
        layer.alert('已达打开窗口数量上限，请先关闭其他窗口在打开新窗口', { icon: 2, title: '警告' });
      } else{
        element.tabAdd(this.layFilter, {
          title: label,
          content: `<iframe src="${ href }" width="100%" height="100%" frameborder="0"></iframe>`,
          id: hash
        });
        this.openTab.push(hash);
        this.change(hash);
      }
    },
    change: function(id) {
      if (id !== this.showHash) {
        this.showHash = id;
        element.tabChange(this.layFilter, id);
      }
    },
    del: function(id) {
      element.tabDelete(this.layFilter, id);
      location.hash = '';
      if (this.showHash === id) this.showHash = '';
      this.openTab.splice(this.openTab.indexOf(id), 1);
    }
  }

  exports('navTab', function({ selector, menuTree, clickClose, layFilter, maxOpens }){
    console.log('欢迎使用！！！');
    const menu = new Menu({ selector, menuTree, clickClose });
    const tab = new Tab({ layFilter, maxOpens, menuTree });
		return { menu, tab };
  });
});